package edu.vt.ece.rt.benchmark.bank;

import java.util.HashMap;
import java.util.Random;

import edu.vt.ece.rt.benchmark.BenchmarkThread;
import edu.vt.ece.rt.dstm.QObject;
import edu.vt.ece.rt.quorum.ClustersManager;
import edu.vt.ece.rt.quorum.Resource;
import edu.vt.ece.rt.quorum.util.Names;
import edu.vt.ece.rt.util.Logger.Logger;



public class Benchmark extends edu.vt.ece.rt.benchmark.Benchmark{

	final int amount = 10000;
	final int transfer = 10;
	private Random random = new Random(hashCode());


	@Override
	protected void createLocalObjects() {

		/* stores the total value in all accounts */

		//		for (int i = 0; i < Integer.getInteger(Names.nodes); i++) {
		//			int id = i;
		BankAccount b = new BankAccount("global", (localObjectsCount) * amount);
		//		}	

		for(int i=0; i < localObjectsCount; i++) {
			//				Logger.debug("Created quorum object " + i);
			try {
				new BankAccount(Integer.toString(i), amount);
			} catch (Throwable e) {
				e.printStackTrace();
			}
		}

		//Add some wait to make sure objects are created successfully

		//			while (Resource.objectsRegistered !=  (localObjectsCount)*(Integer.getInteger(Names.nodes)-1)) {
		//		    	 try {
		//						Thread.sleep(20000L);	
		//					} catch (InterruptedException e) {
		//						e.printStackTrace();
		//					}
		//				
		//			}
		//			System.out.println("Object wait over for "+id);
		//		System.out.println("Resume benchmark for " + id);
	}

	@Override
	protected int getOperandsCount() {	return 2; }

	@Override
	protected Object randomId() {
		int obj = random.nextInt(localObjectsCount - 1);
		return (obj%ClustersManager.nodesCount()) + "-" + obj ;
	}

	@Override

	protected void readOperation(Object... ids) {
		try {
			//			System.out.println("Read op starting " +ClustersManager.baseClusterId);
			Random r = new Random();
			int obj1 = r.nextInt(localObjectsCount - 1);
			String acc1 = Integer.toString(obj1);
			int obj2 = r.nextInt(localObjectsCount - 1);
			while(obj2 == obj1) 
				obj2 = r.nextInt(localObjectsCount - 1);
			String acc2 = Integer.toString(obj2);

			if (ClustersManager.baseClusterId == 0)
				return;	
			//			System.out.println("Read op started  on "+ obj1 + " "+ClustersManager.baseClusterId);
			BankAccount.totalBalanceNested(acc1,acc2, null);
			//			System.out.println("Read op over for "+ClustersManager.baseClusterId);
		} catch (Throwable e) {
			e.printStackTrace();
		} 
	}

	@Override
	protected void writeOperation(Object... ids) {

		try {
			//			Random r = new Random();
			//			int obj1 = r.nextInt(localObjectsCount - 1);
			//			
			//			int obj2 = r.nextInt(localObjectsCount - 1);
			//			while(obj2 == obj1) 
			//				obj2 = r.nextInt(localObjectsCount - 1);
			//			String acc1 = Integer.toString(obj1);
			//			String acc2 = Integer.toString(obj2);
			//			
			////			BankAccount.rootTransaction2(acc1, acc2);
			//			BankAccount.flatNestedTransfer(acc1, acc2);

			BankAccount.closedNestedTransfer(localObjectsCount, nesting);
//			BankAccount.transferObjects(localObjectsCount); 
//			Logger.debug("my read count is "+((BenchmarkThread)Thread.currentThread()).readRequest);
			//			System.out.println("Write op over for "+ClustersManager.baseClusterId);
		} catch (Throwable e) {
			e.printStackTrace();
		} 
	}

	@Override
	protected String getLabel() {
		return "Bank-QR";
	}


	@Override
	protected void checkSanity() {
		//		if (ClustersManager.baseClusterId != 0) {
		//			try {
		//				Thread.sleep(5000);
		//			} catch (InterruptedException e) {
		//				e.printStackTrace();
		//			}
		//			return;
		//		}
		//		try {
		//			Logger.debug("Going to Sleep before Sanity Check");
		//			Thread.sleep(5000);
		//		} catch (InterruptedException e) {
		//			e.printStackTrace();
		//		}

		HashMap<String, Long> map = new HashMap<String, Long>();
		long balance = 0;
		int node = Integer.getInteger(Names.nodes);
		for(int j=0;j<localObjectsCount;j++) {
			try {
				//						long b=((BankAccount) Resource.getQObject(j + "-" + i)).checkBalance();
				String id = Integer.toString(j);
				//						long b= BankAccount.getBalance(id, null);
				long b= ((BankAccount)Resource.getQObject(id)).checkBalance();
				map.put(id, new Long(b));
				//						Logger.debug(id+ " amount " + b); 
				balance +=b;
			} catch (Throwable e) {
				e.printStackTrace();
			}	
		}

		//		int globalSum  = 0;
		//		for(int i=0; i<node; i++){
		//			try {
		//				String id = i+"-global";
		//				long b= BankAccount.getBalance(id, null);
		//				Logger.debug(id+ " amount " + b); 
		//				globalSum +=b;
		//			} catch (Throwable e) {
		//				e.printStackTrace();
		//			}	
		//		}

		//		if(globalSum == balance && balance==node*(localObjectsCount - 1)*amount){
		if(balance==(localObjectsCount)*amount){
			System.out.println("Passed sanity check, balance for "+ClustersManager.baseClusterId+" is "+balance);

			Logger.debug("Passed sanity check, balance for "+ClustersManager.baseClusterId+" is "+balance);
			
			System.exit(0);

			//			Logger.debug("Passed sanity check, global balance for "+ClustersManager.baseClusterId+" is "+globalSum);
			//			for (String id : map.keySet()) {
			//				Logger.debug("Account "+id +" has "+map.get(id));
			//			}
		}
		else{
			System.out.println("Failed sanity check for "+ClustersManager.baseClusterId+" "+
					"\nbalance = "+ balance+
					"\nexpected = "+localObjectsCount*amount);
			Logger.debug("Failed sanity check for "+ClustersManager.baseClusterId+" "+
					"\nbalance = "+ balance+
					"\nexpected = "+localObjectsCount*amount);
			//			Logger.debug("Passed sanity check, global balance for "+ClustersManager.baseClusterId+" is "+globalSum);
		}
	}


	public static boolean checkSanityRegular() {

		HashMap<String, Long> map = new HashMap<String, Long>();
		long balance = 0;
		int node = Integer.getInteger(Names.nodes);
		int  local = Integer.getInteger(Names.objects);
		int amount = 10000;
		for(int j=0;j<local;j++) {
			try {
				//					long b=((BankAccount) Resource.getQObject(j + "-" + i)).checkBalance();
				String id = Integer.toString(j);
				//					long b= BankAccount.getBalance(id, null);
				long b= ((BankAccount)Resource.getQObject(id)).checkBalance();
//				Logger.debug("balance for "+id + " is "+b);
				map.put(id, new Long(b));
				//					Logger.debug(id+ " amount " + b); 
				balance +=b;
			} catch (Throwable e) {
				e.printStackTrace();
			}	
		}

		//	int globalSum  = 0;
		//	for(int i=0; i<node; i++){
		//		try {
		//			String id = i+"-global";
		//			long b= BankAccount.getBalance(id, null);
		//			Logger.debug(id+ " amount " + b); 
		//			globalSum +=b;
		//		} catch (Throwable e) {
		//			e.printStackTrace();
		//		}	
		//	}

		//	if(globalSum == balance && balance==node*(localObjectsCount - 1)*amount){
		if(balance==(local)*(amount)){
//			System.out.println("Passed sanity check, balance for "+ClustersManager.baseClusterId+" is "+balance);

			Logger.debug("Passed sanity check, balance for "+ClustersManager.baseClusterId+" is "+balance);
			
			return true;

			//		Logger.debug("Passed sanity check, global balance for "+ClustersManager.baseClusterId+" is "+globalSum);
			//		for (String id : map.keySet()) {
			//			Logger.debug("Account "+id +" has "+map.get(id));
			//		}
		}
		else{
			System.out.println("Failed sanity check for "+ClustersManager.baseClusterId+" "+
					"\nbalance = "+ balance+
					"\nexpected = "+(local)*(amount));
			Logger.debug("Failed sanity check for "+ClustersManager.baseClusterId+" "+
					"\nbalance = "+ balance+
					"\nexpected = "+(local)*(amount));
			//		Logger.debug("Passed sanity check, global balance for "+ClustersManager.baseClusterId+" is "+globalSum);
			System.out.println("Failure detected");
			return false;
		}
		
	}
}
